#include "ADUFlow.h"
#include "ADUConstant.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
// --------- Custom Shaders -----------------------------------------------
#define STRINGIFY(A)  #A

static const GLchar* _particleShaderVert = STRINGIFY(
attribute vec4 a_position;
uniform float u_size;

void main()
{
    gl_Position = CC_PMatrix * CC_MVMatrix * a_position;
    gl_PointSize = CC_MVMatrix[0][0] * u_size ;
}
);

// Fragment shader
static const GLchar* _particleShaderFrag = STRINGIFY(

#ifdef GL_ES
precision lowp float;
#endif

void main()
{
    gl_FragColor = texture2D(CC_Texture0, gl_PointCoord);
}
);

//
// --------- Custom Shaders ---------
//           Render Texture
//

// Vertex shader
static const GLchar* _renderTextureShaderVert = STRINGIFY(
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;

#ifdef GL_ES
varying lowp vec4 v_fragmentColor;
varying mediump vec2 v_texCoord;
#else
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
#endif

void main()
{
    gl_Position = CC_PMatrix * a_position;
    v_fragmentColor = a_color;
    v_texCoord = a_texCoord;
}
);

// Fragment shader
static const GLchar* _renderTextureShaderFrag = STRINGIFY(

#ifdef GL_ES
precision lowp float;
#endif

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform float u_threshold_discard;
uniform float u_threshold_border;

void main()
{
	    //    gl_FragColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
    vec4 color = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
    if( color.r < u_threshold_discard)
        // black or discard
        color = vec4(0,0,0,0);
    else if( color.r < u_threshold_border)
        // blue for the border
        color = vec4(0.007,0.917,1,1);
    else
        // white for the center
        color = vec4(0.007,0.917,1,1);
    gl_FragColor = color;
});

// ---------End Custom Shaders -----------------------------------------------

#endif // end if CC_PLATFORM_ANDROID || CC_PLATFORM_IOS

Flow::Flow(Node* _layer, Vec2 _position) : mLifetimeRandomizer(PARTICLE_LIFETIME_MIN, PARTICLE_LIFETIME_MAX)
{
    this->mLayer = _layer;
	mWorld = GameManager::getInstance()->mWorld;
	mPosition = _position;
    mEmitter = NULL;
	initPhysics();
    mOffset = Vec2(0,0);
    mOriginPos = this->getPosition();
}

Flow::~Flow() {

}
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

void Flow::draw(Renderer *renderer, const Mat4 &transform, uint32_t transformFlags)
{
    mRenderTexture->beginWithClear(0,0,0,0);
    mCustomCommand.init(_globalZOrder);
    mCustomCommand.func = CC_CALLBACK_0(Flow::onDraw, this, transform, transformFlags);
    renderer->addCommand(&mCustomCommand);
    mRenderTexture->end();
}



void Flow::onDraw(const Mat4 &transform, uint32_t transformFlags)
{
    // transform everything to PTM_RATIO
    Mat4 newMV;
    Mat4::multiply(_modelViewTransform, mRatioTransform, &newMV);
    _glProgramState->apply(newMV);
//#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

    GL::bindTexture2D(mTexture->getName());
    GL::blendFunc(mBlendFunc.src, mBlendFunc.dst);
    int totalParticles = mParticleSystem->GetParticleCount();
    // Update Positions
    glDrawArrays(GL_POINTS, 0, totalParticles);
    CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,totalParticles);
//    CHECK_GL_ERROR_DEBUG();
//#endif

}

// Blend Interface

/// blendFunc getter
const BlendFunc& Flow::getBlendFunc() const
{
    return mBlendFunc;
}
/// blendFunc setter
void Flow::setBlendFunc(const BlendFunc &var)
{
    mBlendFunc = var;
}

bool Flow::initShader(b2ParticleSystem* particleSystem, float ratio)
{
    mParticleSystem = particleSystem;
    mRatioTransform.scale(ratio, ratio, 1);
    mRatio = ratio;

    // own shader

    auto glprogram = GLProgram::createWithByteArrays(_particleShaderVert, _particleShaderFrag);

    GLProgramState *state = GLProgramState::getOrCreateWithGLProgram(glprogram);
    setGLProgramState(state);

    setup();

    auto textureCache = Director::getInstance()->getTextureCache();
    mTexture = textureCache->addImage(IMG_WATER);

//    mBlendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
    mBlendFunc = BlendFunc::ADDITIVE;


    // create a render texture, this is what we are going to draw into
    Size s = Director::getInstance()->getVisibleSize();
	log("DUNGNT: %f",CC_CONTENT_SCALE_FACTOR());
    mRenderTexture = RenderTexture::create(s.width, s.height, Texture2D::PixelFormat::RGBA8888);
    this->addChild(mRenderTexture);
    mRenderTexture->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    mRenderTexture->setPosition(Vec2(s.width/2, s.height/2));

    // Change RenderTexture shader
    auto program = GLProgram::createWithByteArrays(_renderTextureShaderVert, _renderTextureShaderFrag);
    auto programState = GLProgramState::getOrCreateWithGLProgram(program);
    programState->setUniformFloat("u_threshold_discard", 0.15);
    programState->setUniformFloat("u_threshold_border", 0.3);

    mRenderTexture->getSprite()->setGLProgramState(programState);

    return true;
}

void Flow::setup()
{
    auto glProgramState = getGLProgramState();
    float size = mParticleSystem->GetRadius() * 2.0f * SIZE_WATER;

    // Update Positions
    glProgramState->setVertexAttribPointer("a_position", 2, GL_FLOAT, GL_FALSE, 0, mParticleSystem->GetPositionBufferCopy());
    glProgramState->setUniformFloat("u_size", size);

}

#endif // end CC_PLATFORM_ANDROID || CC_PLATFORM_IOS

void Flow::setStartPosition(Vec2& _pos) {
	mPosition = _pos;
}

Vec2 Flow::getStartPosition() {
	return mPosition;
}

void Flow::initPhysics() {
	mWorld = GameManager::getInstance()->mWorld;
	createFaucet();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
	initShader(mParticleSystem,PTM_RATIO);
#endif
}

void Flow::createFaucet() {
	b2ParticleSystemDef particleSystemDef;
	mParticleSystem = mWorld->CreateParticleSystem(&particleSystemDef);
	mParticleSystem->SetRadius(RADIUS_PARTICLE);
	mParticleSystem->SetMaxParticleCount(MAX_PARTICLE);
    mParticleSystem->SetDestructionByAge(true);
	const float32 faucetLength = mParticleSystem->GetRadius() * 1.25f; 
    
    mEmitter = new RadialEmitter(mLayer);
	mEmitter->SetParticleSystem(mParticleSystem);
    mEmitter->SetCallback(&mLifetimeRandomizer);
	mEmitter->SetPosition(b2Vec2(mPosition.x/PTM_RATIO, mPosition.y/PTM_RATIO));
	mEmitter->SetVelocity(FLOW_INIT_SPEED);
	mEmitter->SetDirection(b2Vec2(1,0));
    mEmitter->SetAngle(0);
	mEmitter->SetSize(b2Vec2(0.0f, faucetLength));
	mEmitter->SetEmitRate(150.0f);
	mEmitter->SetParticleFlags(b2_waterParticle | b2_particleContactFilterParticle| b2_fixtureContactFilterParticle);
}

void Flow::updateEmittedParticle(){
    int i,count;
    bool colided;
    float waterHeight;
    b2Vec2 v;
    float value;
    float ra;
    GameManager *manager = GameManager::getInstance();
    waterHeight = GameManager::getInstance()->getWaterHeight();
    b2Vec2 *positions = mParticleSystem->GetPositionBuffer();
    count = mParticleSystem->GetParticleCount();
    for (i = 0; i< count; i++){
        if (positions[i].y < waterHeight){
            colided = manager->GetColisionCache(i);
            if (colided){
                mParticleSystem->DestroyParticle(i);
            }else{
                v = (mParticleSystem->GetVelocityBuffer())[i];
                value = sqrt(v.x*v.x + v.y*v.y)/2;
                ra = rand()%180 - 90;
                ra = ra*PI/180;
                v = 5*b2Vec2(cos(ra), -sin(ra));
                mParticleSystem->SetParticleVelocity(i, v.x, v.y);
            }
            
        }
        
    }
}

void Flow::update(float dt, bool emit) {
    updateEmittedParticle();

    if(emit && mEmitter) {
       mEmitter->update(dt);
       mEmitter->SpeedUp(dt);
    }
   
}


void Flow::resetSpeed(){
	mEmitter->SetVelocity(FLOW_INIT_SPEED);
}

void Flow::setOffset(Vec2 _offset){
    b2Vec2 offset;
    mOffset = _offset;
    offset.x = _offset.x/PTM_RATIO;
    offset.y = _offset.y/PTM_RATIO;
    mParticleSystem->setOffset(offset);
}

void Flow::updatePosition(Vec2& _pos) {
    Vec2 realPos, realStartPos;
    //realPos = mPosition - mOffset;
    realStartPos = _pos - mOffset;
	setStartPosition(realStartPos);
    if(mEmitter) {
        mEmitter->SetPosition(b2Vec2(mPosition.x/PTM_RATIO, mPosition.y/PTM_RATIO));
        #if (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
            mEmitter->updatePosition();
        #endif
    }
}